Code copied from Ferroptosis_heatmaps.R and fixed to run within this repo.

library(reshape2)
library(ggplot2)
library(scales)
library(pheatmap)
library(gplots) # needed for color palettes, redblue(), colorRampPalette()

if(!"ComplexHeatmap" %in% installed.packages()) BiocManager::install("ComplexHeatmap")
library(ComplexHeatmap)
SAVEPLOTS <- FALSE
load("../data/merged_countdata.RData") # object countdata
Ferr <- read.delim("../data/KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
ferr_genes <- Ferr$GeneName  # ferroptosis genes from KEGG (2018)
load("../data/RLD_SC-1,7,10_0,3,8d_20180701.RData")  # object rld

Another ferroptosis gene list from Wikipathways/GSEA: https://www.gsea-msigdb.org/gsea/msigdb/cards/WP_FERROPTOSIS.html File downloaded 2024-01-14 and saved in ../data/WP_FERROPTOSIS.v2023.2.Hs.tsv

ferr_genes_wikipw_raw <- read.table('../data/WP_FERROPTOSIS.v2023.2.Hs.tsv', sep="\t", row.names=1)
ferr_genes_wikipw <- ferr_genes_wikipw_raw["GENE_SYMBOLS",]
ferr_genes_wikipw <- unlist(strsplit(ferr_genes_wikipw,","))
ferr_genes_wikipw <- ferr_genes_wikipw[ferr_genes_wikipw!=""]

Unclear why these genes should be excluded.

excl_genes <- c("ALOX15", "ACSL1", "ACSL3",
                "ACSL5", "ACSL6", "TP53", "TF",
                "CP", "MAP1LC3A", "MAP1LC3C",
                "CYBB")

Define which gene set to use for all plots

object is ferrgenes_4plots. Setting to first item in list will use newer and larger list of genes from wikipathways; setting to item 2 will make output match previously generated plots.

ferr_genes_4plots <- list(ferr_genes_wikipw,ferr_genes)[[1]]

Process raw count data

Ferr <- Ferr[rowSums(is.na(Ferr)) == 0, ]

countdata_Fer <- countdata[ferr_genes_4plots,]  # select genes here

Fer_samples = colsplit(colnames(countdata_Fer), pattern = "_", names = c("Population", "Time", "Replicate"))
Fer_plot = cbind(Fer_samples, t(countdata_Fer))
Fer_melt = melt(data = Fer_plot, id.vars = c("Population", "Time", "Replicate"), measure.vars = ferr_genes_4plots)  # select genes here
Fer_dat = Rmisc::summarySE(Fer_melt, measurevar = "value", groupvars = c("Population", "Time", "variable"))
Fer_dat$Time = as.numeric(gsub("[^[:digit:]]","",Fer_dat$Time))

Low expressed genes

Add genes to excl_genes where all read counts < 16 (log2==4).

excl_genes <- union(excl_genes, rownames(countdata_Fer[which(apply(countdata_Fer, 1, function(x) all(x<=16))),]))

Plot change over time of BRAFi for each subclone

Changed plotting from linear to log2 scale for better visualization of changes. Still raw (non-normalized) counts.

Fer_ggploted <- ggplot(Fer_dat, aes(x=Time, y=log2(value), group = interaction(variable, Population))) + 
  geom_line(linewidth=1.5, aes(color = Population)) + 
  geom_point(size = 1.5, aes(color = Population)) + facet_wrap(~variable, ncol = 5, scales = "free") +
  geom_errorbar(aes(ymin=log2(value-sd), ymax=log2(value+sd), color = Population), width=.2, linewidth=1.5) +
  theme_bw() + xlab("Time (days)") + ylab("Gene Counts") +
  ggtitle("Ferroptosis gene signature") +
  theme(legend.text = element_text(size = 10), legend.position = "right", 
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"),
        axis.title=element_text(size=12,face="bold"))

Fer_ggploted


if(SAVEPLOTS) ggsave("FerroptosisGeneSignature_rawCounts_SKMEL5sublines+treatment.pdf", width = 20, height = 25)

NOTE: This will overwrite objects with new data

Normalized data from DESeq2

normdata_Fer <- as.data.frame(assay(rld))[ferr_genes_4plots,]

Fer_match <- colsplit(colnames(normdata_Fer), pattern = "_", names = c("Population", "Time", "Replicate"))
Fer_plot <- cbind(Fer_match, t(normdata_Fer))
Fer_melt <- melt(data = Fer_plot, id.vars = c("Population", "Time", "Replicate"), measure.vars = unique(colnames(Fer_plot))[4:42])
Fer_dat <- Rmisc::summarySE(Fer_melt, measurevar = "value", groupvars = c("Population", "Time", "variable"))
Fer_dat$Time <- as.numeric(gsub("[^[:digit:]]","",Fer_dat$Time))

Remove excluded genes

Fer_dat <- Fer_dat[!Fer_dat$variable %in% excl_genes,]

Plot change over time of BRAFi for each subclone

Changed plotting from linear to log2 scale for better visualization of changes.

Fer_ggploted <- ggplot(Fer_dat, aes(x=Time, y=value, group = interaction(variable, Population))) + 
  geom_line(linewidth=1.5, aes(color = Population)) + 
  geom_point(size = 1.5, aes(color = Population)) + facet_wrap(~variable, ncol = 5, scales = "free") +
  geom_errorbar(aes(ymin=value-sd, ymax=value+sd, color = Population), width=.2, linewidth=1.5) +
  theme_bw() + xlab("Time (days)") + ylab("Gene Counts") +
  ggtitle("Ferroptosis gene signature") +
  theme(legend.text = element_text(size = 10), legend.position = "right", 
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"),
        axis.title=element_text(size=12,face="bold"))

Fer_ggploted


if(SAVEPLOTS) ggsave("FerroptosisGeneSignature_normCounts_SKMEL5sublines+treatment.pdf", width = 20, height = 25)
# normdata_Fer
samples <- c("SC01_day0", "SC01_day3", "SC01_day8", "SC07_day0", "SC07_day3", "SC07_day8", "SC10_day0", "SC10_day3", "SC10_day8")
test <- sapply(samples, function(x) rowMeans(normdata_Fer[, grep(x, colnames(normdata_Fer))]))
test <- as.data.frame(test[complete.cases(test),])



allFC <- function(DEProc,startcol,endcol){ 
  GE_fold <- DEProc[,-c(startcol:endcol)]
  colvec <- colnames(DEProc)[startcol:endcol]
  
  #Last index is a self comparison and is removed
  for(k in 1:(length(colvec)-1)){
    #Start with column that is 1 away from index 
    for(j in (k+1):length(colvec)){
      compnam <- paste0(colvec[j],"/",colvec[k])
      #Loop through each gene/row  
      for(i in 1:nrow(DEProc)){
        f <- DEProc[i,colvec[j]]
        h <- DEProc[i,colvec[k]]
        GE_fold[i, compnam] <- log2(f/h)
     }
    }
  }

  return(GE_fold)
}

GE_fold <- allFC(test, 1,9)
ImpRat = c("SC01_day3/SC01_day0", "SC01_day8/SC01_day0", 
           "SC07_day3/SC07_day0", "SC07_day8/SC07_day0", 
           "SC10_day3/SC10_day0", "SC10_day8/SC10_day0")
Imp_fold <- GE_fold[,ImpRat]

Ferr <- read.delim("../data/KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
Ferr_fold <- Imp_fold[rownames(Imp_fold) %in% ferr_genes_4plots,]

Ferr_fold$Gene <- rownames(Ferr_fold)

# clean column names
colnames(Ferr_fold) <- gsub("/SC[01][170]_day0","",colnames(Ferr_fold))

Ferr_fold_melt <- melt(Ferr_fold, id.vars = "Gene")
Ferr_fold_melt$Gene <- factor(Ferr_fold_melt$Gene, levels = rev(ferr_genes_4plots))

Ferr_fold_melt <- subset(Ferr_fold_melt, !Gene %in% excl_genes)
myplot <- ggplot(Ferr_fold_melt, aes(variable, Gene, fill = value)) + 
  geom_tile(color = "black") + theme_bw() +
  scale_fill_gradientn(
    colors=c("blue","white","red","red4"),
    values=rescale(c(min(Ferr_fold_melt$value), 0, max(Ferr_fold_melt$value)/2, max(Ferr_fold_melt$value))),
    limits=c(min(Ferr_fold_melt$value),max(Ferr_fold_melt$value)),
    name = "Log2 Fold Change"
  ) + ylab("") + xlab("") + 
  theme(axis.text=element_text(size=10),
        axis.text.x=element_text(angle = 90, hjust = 0),
        axis.title=element_text(size=12),
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank())

myplot

# if(SAVEPLOTS) ggsave("Ferroptosis_FCHM_selected_wide.pdf", width = 8, height = 8)
par(mar=c(8,5,1,1), oma=c(3,1,0,0))
dtp <- as.matrix(Ferr_fold[,1:6])
dtp <- dtp[!rownames(dtp) %in% excl_genes,]
colnames(dtp) <- gsub("day","D",colnames(dtp)) 
mycolors <- colorRampPalette(c("darkblue","blue","white","red","red4"))(50)

gplots::heatmap.2(dtp, dendrogram="none", scale="none", Rowv=FALSE, Colv=FALSE, trace="none", tracecol=NA,
                  col=mycolors, srtCol=0, adjCol=0.5, colsep=c(2,4), offsetRow=-35, adjRow=c(1, 0.5),
                  cexCol = 0.2 + 0.75/log10(ncol(dtp)), cex.lab=0.5,
                  keysize=1, key.title=NA, key.ylab=NA, key.xlab="Log2 fold change", key.par=list(mar=c(6,3,5,1)))

Gene annotations

Manually assembled gene annotations.

annot <- c("Glutathione Metabolism",
           "PUFA",
           "Endosome Ferous Transfer",
           "Ferroportin Export",
           "Autolysosome Ferritin Storage",
           "Ferric Acid Reduction",
           "Heme Iron Transfer",
           "Mitochondria")
annot_col <- c("orange","yellow","green","turquoise","purple","red","brown",grey(0.5))
ferr_genes_annot <- read.csv("../data/ferroptosis_gene_annot.csv")
# ferr_genes_annot[ferr_genes_annot==""] <- NA
annots <- unique(unlist(apply(ferr_genes_annot[,grep("annot",colnames(ferr_genes_annot))],2,unique)))
annots <- annots[annots!=""]
annots <- c(annots,"")
annots_col <- c("yellow","purple",grey(0.5),"black","red","orange","green","turquoise","brown","white")
names(annots_col) <- annots

dat <- Ferr_fold[order(match(Ferr_fold$Gene,ferr_genes_annot$gene_name)),]

fer <- list(genes=Ferr_fold$Gene, 
            fold_exp=dat[,1:6], 
            annot1=ferr_genes_annot[match(dat$Gene,ferr_genes_annot$gene_name),"annot1"],
            annot2=ferr_genes_annot[match(dat$Gene,ferr_genes_annot$gene_name),"annot2"]
            )

Using ComplexHeatmap()

if(SAVEPLOTS)
    pdf("Ferroptosis_heatmap_annot.pdf", width = 8, height = 8)

ht_opt(
    legend_title_gp = gpar(fontsize = 8, fontface = "bold"), 
    legend_labels_gp = gpar(fontsize = 8), 
    heatmap_column_names_gp = gpar(fontsize = 8),
    heatmap_column_title_gp = gpar(fontsize = 10),
    heatmap_row_title_gp = gpar(fontsize = 8)
)

ht_list = Heatmap(as.matrix(fer$fold_exp), name = "Log2 fold expression",
                  width = unit(8, "cm"), 
                  cluster_rows = FALSE, cluster_columns = FALSE,
                  row_names_side = "left", row_names_gp = gpar(fontsize = 8)) +
    Heatmap(fer$annot1, name = "GO:1", col = annots_col, width = unit(5, "mm")) + 
    Heatmap(fer$annot2, name = "GO:2", col = annots_col, width = unit(5, "mm"))

ht_list

LS0tCnRpdGxlOiAiQ29udmVydGluZyBGZXJyb3B0b3NpcyBoZWF0bWFwIGNvZGUgdG8gbm90ZWJvb2siCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IERhcnJlbiBUeXNvbgpkYXRlOiAyMDI0LTAxLTE0Ci0tLQoKQ29kZSBjb3BpZWQgZnJvbSBgRmVycm9wdG9zaXNfaGVhdG1hcHMuUmAgYW5kIGZpeGVkIHRvIHJ1biB3aXRoaW4gdGhpcyByZXBvLgoKCmBgYHtyfQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KGdwbG90cykgIyBuZWVkZWQgZm9yIGNvbG9yIHBhbGV0dGVzLCByZWRibHVlKCksIGNvbG9yUmFtcFBhbGV0dGUoKQoKaWYoISJDb21wbGV4SGVhdG1hcCIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkgQmlvY01hbmFnZXI6Omluc3RhbGwoIkNvbXBsZXhIZWF0bWFwIikKbGlicmFyeShDb21wbGV4SGVhdG1hcCkKYGBgCgoKYGBge3J9ClNBVkVQTE9UUyA8LSBGQUxTRQpgYGAKCgpgYGB7ciBMb2FkIGRhdGF9CmxvYWQoIi4uL2RhdGEvbWVyZ2VkX2NvdW50ZGF0YS5SRGF0YSIpICMgb2JqZWN0IGNvdW50ZGF0YQpGZXJyIDwtIHJlYWQuZGVsaW0oIi4uL2RhdGEvS0VHR0ZlcnJvcHRvc2lzX2hzYTA0MjE2XzA2LTI1LTE4LnR4dCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzID0gRikKZmVycl9nZW5lcyA8LSBGZXJyJEdlbmVOYW1lICAjIGZlcnJvcHRvc2lzIGdlbmVzIGZyb20gS0VHRyAoMjAxOCkKbG9hZCgiLi4vZGF0YS9STERfU0MtMSw3LDEwXzAsMyw4ZF8yMDE4MDcwMS5SRGF0YSIpICAjIG9iamVjdCBybGQKYGBgCgpBbm90aGVyIGZlcnJvcHRvc2lzIGdlbmUgbGlzdCBmcm9tIFdpa2lwYXRod2F5cy9HU0VBOiBodHRwczovL3d3dy5nc2VhLW1zaWdkYi5vcmcvZ3NlYS9tc2lnZGIvY2FyZHMvV1BfRkVSUk9QVE9TSVMuaHRtbApGaWxlIGRvd25sb2FkZWQgMjAyNC0wMS0xNCBhbmQgc2F2ZWQgaW4gYC4uL2RhdGEvV1BfRkVSUk9QVE9TSVMudjIwMjMuMi5Icy50c3ZgCgpgYGB7cn0KZmVycl9nZW5lc193aWtpcHdfcmF3IDwtIHJlYWQudGFibGUoJy4uL2RhdGEvV1BfRkVSUk9QVE9TSVMudjIwMjMuMi5Icy50c3YnLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPTEpCmZlcnJfZ2VuZXNfd2lraXB3IDwtIGZlcnJfZ2VuZXNfd2lraXB3X3Jhd1siR0VORV9TWU1CT0xTIixdCmZlcnJfZ2VuZXNfd2lraXB3IDwtIHVubGlzdChzdHJzcGxpdChmZXJyX2dlbmVzX3dpa2lwdywiLCIpKQpmZXJyX2dlbmVzX3dpa2lwdyA8LSBmZXJyX2dlbmVzX3dpa2lwd1tmZXJyX2dlbmVzX3dpa2lwdyE9IiJdCmBgYAoKClVuY2xlYXIgd2h5IHRoZXNlIGdlbmVzIHNob3VsZCBiZSBleGNsdWRlZC4KYGBge3J9CmV4Y2xfZ2VuZXMgPC0gYygiQUxPWDE1IiwgIkFDU0wxIiwgIkFDU0wzIiwKICAgICAgICAgICAgICAgICJBQ1NMNSIsICJBQ1NMNiIsICJUUDUzIiwgIlRGIiwKICAgICAgICAgICAgICAgICJDUCIsICJNQVAxTEMzQSIsICJNQVAxTEMzQyIsCiAgICAgICAgICAgICAgICAiQ1lCQiIpCmBgYAoKIyMjIERlZmluZSB3aGljaCBnZW5lIHNldCB0byB1c2UgZm9yIGFsbCBwbG90cwpvYmplY3QgaXMgYGZlcnJnZW5lc180cGxvdHNgLiBTZXR0aW5nIHRvIGZpcnN0IGl0ZW0gaW4gbGlzdCB3aWxsIHVzZSBuZXdlciBhbmQgbGFyZ2VyIGxpc3Qgb2YgZ2VuZXMgZnJvbSB3aWtpcGF0aHdheXM7IHNldHRpbmcgdG8gaXRlbSAyIHdpbGwgbWFrZSBvdXRwdXQgbWF0Y2ggcHJldmlvdXNseSBnZW5lcmF0ZWQgcGxvdHMuCmBgYHtyfQpmZXJyX2dlbmVzXzRwbG90cyA8LSBsaXN0KGZlcnJfZ2VuZXNfd2lraXB3LGZlcnJfZ2VuZXMpW1sxXV0KYGBgCgoKIyMjIFByb2Nlc3MgcmF3IGNvdW50IGRhdGEKYGBge3J9CkZlcnIgPC0gRmVycltyb3dTdW1zKGlzLm5hKEZlcnIpKSA9PSAwLCBdCgpjb3VudGRhdGFfRmVyIDwtIGNvdW50ZGF0YVtmZXJyX2dlbmVzXzRwbG90cyxdICAjIHNlbGVjdCBnZW5lcyBoZXJlCgpGZXJfc2FtcGxlcyA9IGNvbHNwbGl0KGNvbG5hbWVzKGNvdW50ZGF0YV9GZXIpLCBwYXR0ZXJuID0gIl8iLCBuYW1lcyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJSZXBsaWNhdGUiKSkKRmVyX3Bsb3QgPSBjYmluZChGZXJfc2FtcGxlcywgdChjb3VudGRhdGFfRmVyKSkKRmVyX21lbHQgPSBtZWx0KGRhdGEgPSBGZXJfcGxvdCwgaWQudmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJSZXBsaWNhdGUiKSwgbWVhc3VyZS52YXJzID0gZmVycl9nZW5lc180cGxvdHMpICAjIHNlbGVjdCBnZW5lcyBoZXJlCkZlcl9kYXQgPSBSbWlzYzo6c3VtbWFyeVNFKEZlcl9tZWx0LCBtZWFzdXJldmFyID0gInZhbHVlIiwgZ3JvdXB2YXJzID0gYygiUG9wdWxhdGlvbiIsICJUaW1lIiwgInZhcmlhYmxlIikpCkZlcl9kYXQkVGltZSA9IGFzLm51bWVyaWMoZ3N1YigiW15bOmRpZ2l0Ol1dIiwiIixGZXJfZGF0JFRpbWUpKQpgYGAKCiMjIyBMb3cgZXhwcmVzc2VkIGdlbmVzCkFkZCBnZW5lcyB0byBgZXhjbF9nZW5lc2Agd2hlcmUgYWxsIHJlYWQgY291bnRzIDwgMTYgKGxvZzI9PTQpLgpgYGB7cn0KZXhjbF9nZW5lcyA8LSB1bmlvbihleGNsX2dlbmVzLCByb3duYW1lcyhjb3VudGRhdGFfRmVyW3doaWNoKGFwcGx5KGNvdW50ZGF0YV9GZXIsIDEsIGZ1bmN0aW9uKHgpIGFsbCh4PD0xNikpKSxdKSkKYGBgCgoKIyMjIFBsb3QgY2hhbmdlIG92ZXIgdGltZSBvZiBCUkFGaSBmb3IgZWFjaCBzdWJjbG9uZQpDaGFuZ2VkIHBsb3R0aW5nIGZyb20gbGluZWFyIHRvIGxvZzIgc2NhbGUgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uIG9mIGNoYW5nZXMuIFN0aWxsIHJhdyAobm9uLW5vcm1hbGl6ZWQpIGNvdW50cy4KYGBge3IgZmlnLmhlaWdodD0yNSwgZmlnLndpZHRoPTIwfQpGZXJfZ2dwbG90ZWQgPC0gZ2dwbG90KEZlcl9kYXQsIGFlcyh4PVRpbWUsIHk9bG9nMih2YWx1ZSksIGdyb3VwID0gaW50ZXJhY3Rpb24odmFyaWFibGUsIFBvcHVsYXRpb24pKSkgKyAKICBnZW9tX2xpbmUobGluZXdpZHRoPTEuNSwgYWVzKGNvbG9yID0gUG9wdWxhdGlvbikpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBhZXMoY29sb3IgPSBQb3B1bGF0aW9uKSkgKyBmYWNldF93cmFwKH52YXJpYWJsZSwgbmNvbCA9IDUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bG9nMih2YWx1ZS1zZCksIHltYXg9bG9nMih2YWx1ZStzZCksIGNvbG9yID0gUG9wdWxhdGlvbiksIHdpZHRoPS4yLCBsaW5ld2lkdGg9MS41KSArCiAgdGhlbWVfYncoKSArIHhsYWIoIlRpbWUgKGRheXMpIikgKyB5bGFiKCJHZW5lIENvdW50cyIpICsKICBnZ3RpdGxlKCJGZXJyb3B0b3NpcyBnZW5lIHNpZ25hdHVyZSIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLCBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCAKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSkKCkZlcl9nZ3Bsb3RlZAoKaWYoU0FWRVBMT1RTKSBnZ3NhdmUoIkZlcnJvcHRvc2lzR2VuZVNpZ25hdHVyZV9yYXdDb3VudHNfU0tNRUw1c3VibGluZXMrdHJlYXRtZW50LnBkZiIsIHdpZHRoID0gMjAsIGhlaWdodCA9IDI1KQpgYGAKCgojIyMgTk9URTogVGhpcyB3aWxsIG92ZXJ3cml0ZSBvYmplY3RzIHdpdGggbmV3IGRhdGEKTm9ybWFsaXplZCBkYXRhIGZyb20gREVTZXEyCmBgYHtyfQpub3JtZGF0YV9GZXIgPC0gYXMuZGF0YS5mcmFtZShhc3NheShybGQpKVtmZXJyX2dlbmVzXzRwbG90cyxdCgpGZXJfbWF0Y2ggPC0gY29sc3BsaXQoY29sbmFtZXMobm9ybWRhdGFfRmVyKSwgcGF0dGVybiA9ICJfIiwgbmFtZXMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIikpCkZlcl9wbG90IDwtIGNiaW5kKEZlcl9tYXRjaCwgdChub3JtZGF0YV9GZXIpKQpGZXJfbWVsdCA8LSBtZWx0KGRhdGEgPSBGZXJfcGxvdCwgaWQudmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJSZXBsaWNhdGUiKSwgbWVhc3VyZS52YXJzID0gdW5pcXVlKGNvbG5hbWVzKEZlcl9wbG90KSlbNDo0Ml0pCkZlcl9kYXQgPC0gUm1pc2M6OnN1bW1hcnlTRShGZXJfbWVsdCwgbWVhc3VyZXZhciA9ICJ2YWx1ZSIsIGdyb3VwdmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJ2YXJpYWJsZSIpKQpGZXJfZGF0JFRpbWUgPC0gYXMubnVtZXJpYyhnc3ViKCJbXls6ZGlnaXQ6XV0iLCIiLEZlcl9kYXQkVGltZSkpCmBgYAoKIyMjIFJlbW92ZSBleGNsdWRlZCBnZW5lcwpgYGB7cn0KRmVyX2RhdCA8LSBGZXJfZGF0WyFGZXJfZGF0JHZhcmlhYmxlICVpbiUgZXhjbF9nZW5lcyxdCmBgYAoKIyMjIFBsb3QgY2hhbmdlIG92ZXIgdGltZSBvZiBCUkFGaSBmb3IgZWFjaCBzdWJjbG9uZQpDaGFuZ2VkIHBsb3R0aW5nIGZyb20gbGluZWFyIHRvIGxvZzIgc2NhbGUgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uIG9mIGNoYW5nZXMuCmBgYHtyIGZpZy5oZWlnaHQ9MjUsIGZpZy53aWR0aD0yMH0KRmVyX2dncGxvdGVkIDwtIGdncGxvdChGZXJfZGF0LCBhZXMoeD1UaW1lLCB5PXZhbHVlLCBncm91cCA9IGludGVyYWN0aW9uKHZhcmlhYmxlLCBQb3B1bGF0aW9uKSkpICsgCiAgZ2VvbV9saW5lKGxpbmV3aWR0aD0xLjUsIGFlcyhjb2xvciA9IFBvcHVsYXRpb24pKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgYWVzKGNvbG9yID0gUG9wdWxhdGlvbikpICsgZmFjZXRfd3JhcCh+dmFyaWFibGUsIG5jb2wgPSA1LCBzY2FsZXMgPSAiZnJlZSIpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPXZhbHVlLXNkLCB5bWF4PXZhbHVlK3NkLCBjb2xvciA9IFBvcHVsYXRpb24pLCB3aWR0aD0uMiwgbGluZXdpZHRoPTEuNSkgKwogIHRoZW1lX2J3KCkgKyB4bGFiKCJUaW1lIChkYXlzKSIpICsgeWxhYigiR2VuZSBDb3VudHMiKSArCiAgZ2d0aXRsZSgiRmVycm9wdG9zaXMgZ2VuZSBzaWduYXR1cmUiKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIiksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIikpCgpGZXJfZ2dwbG90ZWQKCmlmKFNBVkVQTE9UUykgZ2dzYXZlKCJGZXJyb3B0b3Npc0dlbmVTaWduYXR1cmVfbm9ybUNvdW50c19TS01FTDVzdWJsaW5lcyt0cmVhdG1lbnQucGRmIiwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMjUpCmBgYAoKCmBgYHtyfQojIG5vcm1kYXRhX0ZlcgpzYW1wbGVzIDwtIGMoIlNDMDFfZGF5MCIsICJTQzAxX2RheTMiLCAiU0MwMV9kYXk4IiwgIlNDMDdfZGF5MCIsICJTQzA3X2RheTMiLCAiU0MwN19kYXk4IiwgIlNDMTBfZGF5MCIsICJTQzEwX2RheTMiLCAiU0MxMF9kYXk4IikKdGVzdCA8LSBzYXBwbHkoc2FtcGxlcywgZnVuY3Rpb24oeCkgcm93TWVhbnMobm9ybWRhdGFfRmVyWywgZ3JlcCh4LCBjb2xuYW1lcyhub3JtZGF0YV9GZXIpKV0pKQp0ZXN0IDwtIGFzLmRhdGEuZnJhbWUodGVzdFtjb21wbGV0ZS5jYXNlcyh0ZXN0KSxdKQoKCgphbGxGQyA8LSBmdW5jdGlvbihERVByb2Msc3RhcnRjb2wsZW5kY29sKXsgCiAgR0VfZm9sZCA8LSBERVByb2NbLC1jKHN0YXJ0Y29sOmVuZGNvbCldCiAgY29sdmVjIDwtIGNvbG5hbWVzKERFUHJvYylbc3RhcnRjb2w6ZW5kY29sXQogIAogICNMYXN0IGluZGV4IGlzIGEgc2VsZiBjb21wYXJpc29uIGFuZCBpcyByZW1vdmVkCiAgZm9yKGsgaW4gMToobGVuZ3RoKGNvbHZlYyktMSkpewogICAgI1N0YXJ0IHdpdGggY29sdW1uIHRoYXQgaXMgMSBhd2F5IGZyb20gaW5kZXggCiAgICBmb3IoaiBpbiAoaysxKTpsZW5ndGgoY29sdmVjKSl7CiAgICAgIGNvbXBuYW0gPC0gcGFzdGUwKGNvbHZlY1tqXSwiLyIsY29sdmVjW2tdKQogICAgICAjTG9vcCB0aHJvdWdoIGVhY2ggZ2VuZS9yb3cgIAogICAgICBmb3IoaSBpbiAxOm5yb3coREVQcm9jKSl7CiAgICAgICAgZiA8LSBERVByb2NbaSxjb2x2ZWNbal1dCiAgICAgICAgaCA8LSBERVByb2NbaSxjb2x2ZWNba11dCiAgICAgICAgR0VfZm9sZFtpLCBjb21wbmFtXSA8LSBsb2cyKGYvaCkKICAgICB9CiAgICB9CiAgfQoKICByZXR1cm4oR0VfZm9sZCkKfQoKR0VfZm9sZCA8LSBhbGxGQyh0ZXN0LCAxLDkpCkltcFJhdCA9IGMoIlNDMDFfZGF5My9TQzAxX2RheTAiLCAiU0MwMV9kYXk4L1NDMDFfZGF5MCIsIAogICAgICAgICAgICJTQzA3X2RheTMvU0MwN19kYXkwIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAKICAgICAgICAgICAiU0MxMF9kYXkzL1NDMTBfZGF5MCIsICJTQzEwX2RheTgvU0MxMF9kYXkwIikKSW1wX2ZvbGQgPC0gR0VfZm9sZFssSW1wUmF0XQoKRmVyciA8LSByZWFkLmRlbGltKCIuLi9kYXRhL0tFR0dGZXJyb3B0b3Npc19oc2EwNDIxNl8wNi0yNS0xOC50eHQiLCBoZWFkZXI9VCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCkZlcnJfZm9sZCA8LSBJbXBfZm9sZFtyb3duYW1lcyhJbXBfZm9sZCkgJWluJSBmZXJyX2dlbmVzXzRwbG90cyxdCgpGZXJyX2ZvbGQkR2VuZSA8LSByb3duYW1lcyhGZXJyX2ZvbGQpCgojIGNsZWFuIGNvbHVtbiBuYW1lcwpjb2xuYW1lcyhGZXJyX2ZvbGQpIDwtIGdzdWIoIi9TQ1swMV1bMTcwXV9kYXkwIiwiIixjb2xuYW1lcyhGZXJyX2ZvbGQpKQpgYGAKCiMjIyAKYGBge3J9CkZlcnJfZm9sZF9tZWx0IDwtIG1lbHQoRmVycl9mb2xkLCBpZC52YXJzID0gIkdlbmUiKQpGZXJyX2ZvbGRfbWVsdCRHZW5lIDwtIGZhY3RvcihGZXJyX2ZvbGRfbWVsdCRHZW5lLCBsZXZlbHMgPSByZXYoZmVycl9nZW5lc180cGxvdHMpKQoKRmVycl9mb2xkX21lbHQgPC0gc3Vic2V0KEZlcnJfZm9sZF9tZWx0LCAhR2VuZSAlaW4lIGV4Y2xfZ2VuZXMpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9Cm15cGxvdCA8LSBnZ3Bsb3QoRmVycl9mb2xkX21lbHQsIGFlcyh2YXJpYWJsZSwgR2VuZSwgZmlsbCA9IHZhbHVlKSkgKyAKICBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siKSArIHRoZW1lX2J3KCkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKAogICAgY29sb3JzPWMoImJsdWUiLCJ3aGl0ZSIsInJlZCIsInJlZDQiKSwKICAgIHZhbHVlcz1yZXNjYWxlKGMobWluKEZlcnJfZm9sZF9tZWx0JHZhbHVlKSwgMCwgbWF4KEZlcnJfZm9sZF9tZWx0JHZhbHVlKS8yLCBtYXgoRmVycl9mb2xkX21lbHQkdmFsdWUpKSksCiAgICBsaW1pdHM9YyhtaW4oRmVycl9mb2xkX21lbHQkdmFsdWUpLG1heChGZXJyX2ZvbGRfbWVsdCR2YWx1ZSkpLAogICAgbmFtZSA9ICJMb2cyIEZvbGQgQ2hhbmdlIgogICkgKyB5bGFiKCIiKSArIHhsYWIoIiIpICsgCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAwKSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQoKbXlwbG90CiMgaWYoU0FWRVBMT1RTKSBnZ3NhdmUoIkZlcnJvcHRvc2lzX0ZDSE1fc2VsZWN0ZWRfd2lkZS5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCmBgYAoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9CnBhcihtYXI9Yyg4LDUsMSwxKSwgb21hPWMoMywxLDAsMCkpCmR0cCA8LSBhcy5tYXRyaXgoRmVycl9mb2xkWywxOjZdKQpkdHAgPC0gZHRwWyFyb3duYW1lcyhkdHApICVpbiUgZXhjbF9nZW5lcyxdCmNvbG5hbWVzKGR0cCkgPC0gZ3N1YigiZGF5IiwiRCIsY29sbmFtZXMoZHRwKSkgCm15Y29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiZGFya2JsdWUiLCJibHVlIiwid2hpdGUiLCJyZWQiLCJyZWQ0IikpKDUwKQoKZ3Bsb3RzOjpoZWF0bWFwLjIoZHRwLCBkZW5kcm9ncmFtPSJub25lIiwgc2NhbGU9Im5vbmUiLCBSb3d2PUZBTFNFLCBDb2x2PUZBTFNFLCB0cmFjZT0ibm9uZSIsIHRyYWNlY29sPU5BLAogICAgICAgICAgICAgICAgICBjb2w9bXljb2xvcnMsIHNydENvbD0wLCBhZGpDb2w9MC41LCBjb2xzZXA9YygyLDQpLCBvZmZzZXRSb3c9LTM1LCBhZGpSb3c9YygxLCAwLjUpLAogICAgICAgICAgICAgICAgICBjZXhDb2wgPSAwLjIgKyAwLjc1L2xvZzEwKG5jb2woZHRwKSksIGNleC5sYWI9MC41LAogICAgICAgICAgICAgICAgICBrZXlzaXplPTEsIGtleS50aXRsZT1OQSwga2V5LnlsYWI9TkEsIGtleS54bGFiPSJMb2cyIGZvbGQgY2hhbmdlIiwga2V5LnBhcj1saXN0KG1hcj1jKDYsMyw1LDEpKSkKYGBgCgojIyMgR2VuZSBhbm5vdGF0aW9ucwpNYW51YWxseSBhc3NlbWJsZWQgZ2VuZSBhbm5vdGF0aW9ucy4KYGBgCmFubm90IDwtIGMoIkdsdXRhdGhpb25lIE1ldGFib2xpc20iLAogICAgICAgICAgICJQVUZBIiwKICAgICAgICAgICAiRW5kb3NvbWUgRmVyb3VzIFRyYW5zZmVyIiwKICAgICAgICAgICAiRmVycm9wb3J0aW4gRXhwb3J0IiwKICAgICAgICAgICAiQXV0b2x5c29zb21lIEZlcnJpdGluIFN0b3JhZ2UiLAogICAgICAgICAgICJGZXJyaWMgQWNpZCBSZWR1Y3Rpb24iLAogICAgICAgICAgICJIZW1lIElyb24gVHJhbnNmZXIiLAogICAgICAgICAgICJNaXRvY2hvbmRyaWEiKQphbm5vdF9jb2wgPC0gYygib3JhbmdlIiwieWVsbG93IiwiZ3JlZW4iLCJ0dXJxdW9pc2UiLCJwdXJwbGUiLCJyZWQiLCJicm93biIsZ3JleSgwLjUpKQpgYGAKCgpgYGB7cn0KZmVycl9nZW5lc19hbm5vdCA8LSByZWFkLmNzdigiLi4vZGF0YS9mZXJyb3B0b3Npc19nZW5lX2Fubm90LmNzdiIpCmZlcnJfZ2VuZXNfYW5ub3RbZmVycl9nZW5lc19hbm5vdD09IiJdIDwtIE5BCmFubm90cyA8LSB1bmlxdWUodW5saXN0KGFwcGx5KGZlcnJfZ2VuZXNfYW5ub3RbLGdyZXAoImFubm90Iixjb2xuYW1lcyhmZXJyX2dlbmVzX2Fubm90KSldLDIsdW5pcXVlKSkpCmFubm90cyA8LSBhbm5vdHNbIWlzLm5hKGFubm90cyldCmFubm90c19jb2wgPC0gYygieWVsbG93IiwicHVycGxlIixncmV5KDAuNSksImJsYWNrIiwicmVkIiwib3JhbmdlIiwiZ3JlZW4iLCJ0dXJxdW9pc2UiLCJicm93biIpCm5hbWVzKGFubm90c19jb2wpIDwtIGFubm90cwoKZGF0IDwtIEZlcnJfZm9sZFtvcmRlcihtYXRjaChGZXJyX2ZvbGQkR2VuZSxmZXJyX2dlbmVzX2Fubm90JGdlbmVfbmFtZSkpLF0KCmZlciA8LSBsaXN0KGdlbmVzPUZlcnJfZm9sZCRHZW5lLCAKICAgICAgICAgICAgZm9sZF9leHA9ZGF0WywxOjZdLCAKICAgICAgICAgICAgYW5ub3QxPWZlcnJfZ2VuZXNfYW5ub3RbbWF0Y2goZGF0JEdlbmUsZmVycl9nZW5lc19hbm5vdCRnZW5lX25hbWUpLCJhbm5vdDEiXSwKICAgICAgICAgICAgYW5ub3QyPWZlcnJfZ2VuZXNfYW5ub3RbbWF0Y2goZGF0JEdlbmUsZmVycl9nZW5lc19hbm5vdCRnZW5lX25hbWUpLCJhbm5vdDIiXQogICAgICAgICAgICApCmBgYAoKCgojIyBVc2luZyBgQ29tcGxleEhlYXRtYXAoKWAKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CmlmKFNBVkVQTE9UUykKICAgIHBkZigiRmVycm9wdG9zaXNfaGVhdG1hcF9hbm5vdC5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDgpCgpodF9vcHQoCiAgICBsZWdlbmRfdGl0bGVfZ3AgPSBncGFyKGZvbnRzaXplID0gOCwgZm9udGZhY2UgPSAiYm9sZCIpLCAKICAgIGxlZ2VuZF9sYWJlbHNfZ3AgPSBncGFyKGZvbnRzaXplID0gOCksIAogICAgaGVhdG1hcF9jb2x1bW5fbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplID0gOCksCiAgICBoZWF0bWFwX2NvbHVtbl90aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksCiAgICBoZWF0bWFwX3Jvd190aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgPSA4KQopCgpodF9saXN0ID0gSGVhdG1hcChhcy5tYXRyaXgoZmVyJGZvbGRfZXhwKSwgbmFtZSA9ICJMb2cyIGZvbGQgZXhwcmVzc2lvbiIsCiAgICAgICAgICAgICAgICAgIHdpZHRoID0gdW5pdCg4LCAiY20iKSwgCiAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgcm93X25hbWVzX3NpZGUgPSAibGVmdCIsIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSA4KSkgKwogICAgSGVhdG1hcChmZXIkYW5ub3QxLCBuYW1lID0gIkdPOjEiLCBjb2wgPSBhbm5vdHNfY29sLCB3aWR0aCA9IHVuaXQoNSwgIm1tIikpICsgCiAgICBIZWF0bWFwKGZlciRhbm5vdDIsIG5hbWUgPSAiR086MiIsIGNvbCA9IGFubm90c19jb2wsIHdpZHRoID0gdW5pdCg1LCAibW0iKSkKCmh0X2xpc3QKYGBgCgoK